#include "bit.h"
#include "timer.h"

/* the list of software timer
   O -> O -> O ... O -> null */
static volatile mx_timer_pt pbase = mx_null;

/* global time (ms), it's readonly for other components */
static volatile mx_uint64_t gtime = 0;

/**
 * mount a timer to system
 * @param ptimer: the timer descriptor
 * @param mode: the timer mode
 * @param reload: the reload value
 * @param func: the callback function, it can be null
 * @return: void
 */
void mx_timer_mount (mx_timer_pt ptimer, mx_timer_mode_t mode,
                     mx_usize_t reload, mx_timer_func_t func)
{
    ptimer->status = MX_TIMER_STATUS_DISABLE;
    ptimer->mode = mode;
    ptimer->reload = reload;
    ptimer->current = 0;
    ptimer->func = func;
    if (pbase == mx_null)
    {
        ptimer->next = mx_null;
        pbase = ptimer;
    }
    else
    {
        ptimer->next = pbase;
        pbase = ptimer;
    }
}

/**
 * umount a timer from system
 * @param ptimer: the timer descriptor
 * @return: void
 */
void mx_timer_umount (mx_timer_pt ptimer)
{
    if (pbase == ptimer)
    {
        pbase = ptimer->next;
    }
    else
    {
        for (mx_timer_pt pnext = pbase; pnext != mx_null; pnext = pnext->next)
        {
            if (pnext->next == ptimer)
            {
                pnext->next = ptimer->next;
                break;
            }
        }
    }
}

/**
 * init the spcified timer
 * @param ptimer: the timer descriptor
 * @param reload: the reload value
 * @return: void
 */
void mx_timer_init (mx_timer_pt ptimer, mx_usize_t reload)
{
    ptimer->reload = reload;
    ptimer->current = 0;
    ptimer->status = MX_TIMER_STATUS_DISABLE;
}

/**
 * reset the spcified timer and stop it
 * @param ptimer: the timer descriptor
 * @return: void
 */
void mx_timer_reset (mx_timer_pt ptimer)
{
    ptimer->current = 0;
    ptimer->status = MX_TIMER_STATUS_DISABLE;
}

/**
 * reset the current tick of timer and start it
 * @param ptimer: the timer descriptor
 * @return: void
 */
void mx_timer_restart (mx_timer_pt ptimer)
{
    ptimer->current = 0;
    ptimer->status = MX_TIMER_STATUS_ENABLE;
}

/**
 * start the timer when the 'begin tick' not qeual to 0
 * @param ptimer: the timer descriptor
 * @return: void
 */
void mx_timer_start (mx_timer_pt ptimer)
{
    ptimer->status = MX_TIMER_STATUS_ENABLE;
}

/**
 * pause the timer while it is runing
 * @param ptimer: the timer descriptor
 * @return: void
 */
void mx_timer_pause (mx_timer_pt ptimer)
{
    if (ptimer->status == MX_TIMER_STATUS_ENABLE)
    {
        ptimer->status = MX_TIMER_STATUS_PAUSE;
    }
}

/**
 * stop the timer while it is runing
 * @param ptimer: the timer descriptor
 * @return: void
 */
void mx_timer_stop (mx_timer_pt ptimer)
{
    if (ptimer->status == MX_TIMER_STATUS_ENABLE)
    {
        ptimer->status = MX_TIMER_STATUS_DISABLE;
    }
}

/**
 * start the timer with new configuration
 * @param ptimer: the timer descriptor
 * @param reload: the new reload value
 * @return: void
 */
void mx_timer_launch (mx_timer_pt ptimer, mx_usize_t reload)
{
    ptimer->reload = reload;
    ptimer->current = 0;
    ptimer->status = MX_TIMER_STATUS_ENABLE;
}

/**
 * bind a call back function to the timer
 * @param ptimer: the timer descriptor
 * @param func: the callback function, it can be null
 * @return: void
 */
void mx_timer_attach (mx_timer_pt ptimer, mx_timer_func_t func)
{
    ptimer->func = func;
}

/**
 * get the status of timer
 * @param ptimer: the timer descriptor
 * @return: the timer status
 */
mx_timer_status_t mx_timer_status (mx_timer_pt ptimer)
{
    return (mx_timer_status_t)ptimer->status;
}

/**
 * check whether the timer is timeout
 * @param ptimer: the timer descriptor
 * @return: void
 */
mx_bool_t mx_timer_istimeout (mx_timer_pt ptimer)
{
    return (ptimer->status == MX_TIMER_STATUS_TIMEOUT);
}

/**
 * check whether the timer is enable
 * @param ptimer: the timer descriptor
 * @return: void
 */
mx_bool_t mx_timer_isenable (mx_timer_pt ptimer)
{
    return (ptimer->status == MX_TIMER_STATUS_ENABLE);
}

/**
 * check whether the timer is disable
 * @param ptimer: the timer descriptor
 * @return: void
 */
mx_bool_t mx_timer_isdisable (mx_timer_pt ptimer)
{
    return (ptimer->status == MX_TIMER_STATUS_DISABLE);
}

/**
 * check whether the timer is paused
 * @param ptimer: the timer descriptor
 * @return: void
 */
mx_bool_t mx_timer_ispause (mx_timer_pt ptimer)
{
    return (ptimer->status == MX_TIMER_STATUS_PAUSE);
}

/**
 * get current count of the specified timer
 * @param ptimer: the timer descriptor
 * @return: current count
 */
mx_usize_t mx_timer_getcurrent (mx_timer_pt ptimer)
{
    return ptimer->current;
}

/**
 * change the mode of specified timer
 * @param ptimer: the timer descriptor
 * @param mode: the timer mode
 * @return: void
 */
void mx_timer_chmode (mx_timer_pt ptimer, mx_timer_mode_t mode)
{
    ptimer->mode = mode;
}

/**
 * get the global time, the cumulative running milliseconds
 * @param: void
 * @return: the global time
 * @note: read the `gtime(64-bit)` need 2 cpu cycle in 32-bit system
 */
mx_uint64_t mx_timer_globaltime ()
{
    return gtime;
}

/**
 * timer driver function
 * @note: call this funciton in the timer interrupt
 * @param: void
 * @return: void
 */
void mx_timer_tick ()
{
    /* global time accumulation */
    gtime += 1000 / MX_TIMER_PERIOD;
    /* processing timer list */
    for (mx_timer_pt pnext = pbase; pnext != mx_null; pnext = pnext->next)
    {
        if (pnext->status == MX_TIMER_STATUS_ENABLE)
        {
            pnext->current ++;
            if (pnext->current > pnext->reload)
            {
                if (pnext->mode == MX_TIMER_MODE_ONESHOT)
                {
                    pnext->status = MX_TIMER_STATUS_TIMEOUT;
                }
                else
                {
                    pnext->current = 0;
                }
                if (pnext->func != mx_null)
                {
                    pnext->func(pnext);
                }
            }
        }
    }
}
